package com.lu.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lu.mapper.SysApprovalConfigMapper;
import com.lu.mapper.SysApprovalLogMapper;
import com.lu.mapper.SysUserMapper;
import com.lu.model.domain.ProcessApprovalLog;
import com.lu.model.domain.ProcessApprovalWaitList;
import com.lu.model.entity.SysApprovalConfig;
import com.lu.model.entity.SysApprovalLog;
import com.lu.model.entity.SysRole;
import com.lu.model.entity.SysUser;
import com.lu.model.enums.YesOrNoEnum;
import com.lu.model.exception.LuBootAssert;
import com.lu.model.request.ProcessApprovalQueryRequest;
import com.lu.model.request.ProcessApprovalSubmitRequest;
import com.lu.model.request.ProcessApprovalUpdateInfoRequest;
import com.lu.service.SysApprovalService;
import com.lu.utils.SecurityUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统审批日志 服务实现类
 * </p>
 *
 * @author currentCodeAuthor
 * @since 2022-08-01
 */
@RequiredArgsConstructor
@Service
public class SysApprovalServiceImpl implements SysApprovalService {

  private final SysApprovalConfigMapper configMapper;
  private final SysApprovalLogMapper logMapper;
  private final SysUserMapper userMapper;

  @PostConstruct
  public void init(){
     refreshConfig();
  }

  @Override
  public Map<String, SysApprovalConfig> refreshConfig() {
    List<SysApprovalConfig> configList = this.configList();
    for (SysApprovalConfig config : configList) {
      QueryWrapper<SysApprovalConfig> configQueryWrapper = new QueryWrapper<>();
      configQueryWrapper.eq("table_name", config.getTableName());
      configQueryWrapper.orderByAsc("id");
      List<SysApprovalConfig> configs = configMapper.selectList(configQueryWrapper);
      SysApprovalConfig.build(configs);
    }
    return SysApprovalConfig.CONFIG_MAP;
  }

  @Override
  public List<SysApprovalConfig> configList() {
    QueryWrapper<SysApprovalConfig> configQueryWrapper = new QueryWrapper<>();
    configQueryWrapper.select("title", "table_name");
    configQueryWrapper.groupBy("table_name");
    return configMapper.selectList(configQueryWrapper);
  }

  @Lock4j(keys = {"#request.tableName", "#request.objId"})
  @Transactional(rollbackFor = Exception.class)
  @Override
  public String submit(ProcessApprovalSubmitRequest request) {
    LuBootAssert.isTrue(ObjectUtil.isNotEmpty(request.getStatus())
        && Arrays.stream(YesOrNoEnum.values()).anyMatch(o -> o.getValue() == request.getStatus()), "无效的状态参数");
    //审批配置
    SysApprovalConfig config = SysApprovalConfig.CONFIG_MAP.get(request.getTableName());
    LuBootAssert.isTrue(config != null, "审核失败，流程未配置");
    //当前节点
    SysApprovalConfig approvalConfig = SysApprovalConfig.get(config, request.getConfigId());
    LuBootAssert.isTrue(approvalConfig != null, "审核失败，请检查流程配置");
    //尾节点
    SysApprovalConfig endConfig = SysApprovalConfig.get(config, SysApprovalConfig.END_NODE_ID);
    LuBootAssert.isTrue(endConfig != null, "审核失败，请检查流程配置");
    SysUser currentUser = SecurityUtil.getCurrentUser();
    List<String> approvedRoleIds = null;
    List<String> currentUserRoleIds = null;
    if(ObjectUtil.isNotEmpty(approvalConfig.getApprovedRoleIds()) && ObjectUtil.isNotEmpty(currentUser.getRoleList())){
      approvedRoleIds = Arrays.stream(approvalConfig.getApprovedRoleIds().split(StrUtil.COMMA)).collect(Collectors.toList());
      currentUserRoleIds = currentUser.getRoleList().stream().map(o -> o.getId().toString()).collect(Collectors.toList());
      approvedRoleIds.retainAll(currentUserRoleIds);
    }
    //检查审批权限
    LuBootAssert.isTrue((ObjectUtil.isNotEmpty(approvalConfig.getApprovedUserIds()) && currentUser.getId() != null
        && Arrays.stream(approvalConfig.getApprovedUserIds().split(StrUtil.COMMA)).anyMatch(o -> o.equals(currentUser.getId().toString())))
        || (ObjectUtil.isNotEmpty(approvedRoleIds)), "无审批权限");
    //检查是否已经审批完毕
    int count = logMapper.submitCheck(config, request.getObjId());
    LuBootAssert.isFalse(count > 0, "已审批完成");
    //记录日志
    SysApprovalLog approvalLog = new SysApprovalLog();
    approvalLog.setApprovalConfigId(approvalConfig.getId());
    approvalLog.setApprovalConfigNextId(approvalConfig.getNextId());
    approvalLog.setTableName(request.getTableName());
    approvalLog.setObjId(request.getObjId());
    approvalLog.setStatus(request.getStatus());
    approvalLog.setDescription(request.getDescription());
    approvalLog.setUserId(currentUser.getId());
    approvalLog.setRoleIds(ObjectUtil.isEmpty(currentUserRoleIds) ? null : StrUtil.join(StrUtil.COMMA, currentUserRoleIds));
    logMapper.insert(approvalLog);
    String res = approvalConfig.getId() + ":" + approvalLog.getId();
    //判断审批是否失败，若失败则直接修改业务表
    if(request.getStatus() == YesOrNoEnum.NO.getValue()){
      int rs = logMapper.submit(endConfig, request.getObjId(), request.getStatus());
      //修改流程日志表结束
      SysApprovalLog log = new SysApprovalLog();
      log.setEnd(YesOrNoEnum.YES.getValue());
      QueryWrapper<SysApprovalLog> logQueryWrapper = new QueryWrapper<>();
      logQueryWrapper.eq("table_name", request.getTableName());
      logQueryWrapper.eq("obj_id", request.getObjId());
      logQueryWrapper.eq("end", YesOrNoEnum.NO.getValue());
      logMapper.update(log, logQueryWrapper);
      return res + ":" + rs;
    }
    if(approvalConfig.getNextId() == endConfig.getId()){  //下一个是最后一个节点了
      //修改业务表状态，流程结束
      int rs = logMapper.submit(endConfig, request.getObjId(), request.getStatus());
      //修改流程日志表结束
      SysApprovalLog log = new SysApprovalLog();
      log.setEnd(YesOrNoEnum.YES.getValue());
      QueryWrapper<SysApprovalLog> logQueryWrapper = new QueryWrapper<>();
      logQueryWrapper.eq("table_name", request.getTableName());
      logQueryWrapper.eq("obj_id", request.getObjId());
      logQueryWrapper.eq("end", YesOrNoEnum.NO.getValue());
      logMapper.update(log, logQueryWrapper);
      return res + ":" + rs;
    }
    return res;
  }

  @Override
  public <T> T updateInfo(ProcessApprovalUpdateInfoRequest request) {
    //审批配置
    SysApprovalConfig config = SysApprovalConfig.CONFIG_MAP.get(request.getTableName());
    LuBootAssert.isTrue(config != null, "审核失败，流程未配置");
    //尾节点
    SysApprovalConfig endConfig = SysApprovalConfig.get(config, SysApprovalConfig.END_NODE_ID);
    LuBootAssert.isTrue(endConfig != null, "审核失败，请检查流程配置");
    //
    ProcessApprovalQueryRequest queryRequest = new ProcessApprovalQueryRequest();
    queryRequest.setObjId(request.getObjId());
    queryRequest.setTableName(request.getTableName());
    Object obj = get(queryRequest);
    cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(obj);
    String targetField = endConfig.getTargetField();
    Integer status = jsonObject.getInt(targetField);
    LuBootAssert.isFalse( status == endConfig.getWaitSign(), "审核中，不可修改");
    LuBootAssert.isFalse( status == endConfig.getPassSign(), "已审核通过，不可修改");
    LuBootAssert.isFalse( status == endConfig.getEnableSign(), "已审核通过，不可修改");
    LuBootAssert.isFalse( status == endConfig.getDisableSign(), "管理员已禁用您的账户，请联系管理员");
    request.setUpdateUserId(SecurityUtil.getCurrentUser().getId());
    Map<String, Object> params = new HashMap<>();
    Map<String, Object> requestParams = request.getParams();
    requestParams.forEach((k ,v) -> {
      params.put(StrUtil.toUnderlineCase(k), v);
    });
    request.setParams(params);
    logMapper.updateBusinessObj(request, endConfig);
    return get(queryRequest);
  }

  @Override
  public Page<ProcessApprovalLog> pageList(ProcessApprovalQueryRequest request) {
    SysApprovalConfig approvalConfig = SysApprovalConfig.CONFIG_MAP.get(request.getTableName());
    LuBootAssert.isTrue(approvalConfig != null, "流程未配置");
    SysApprovalConfig config = SysApprovalConfig.get(approvalConfig, SysApprovalConfig.END_NODE_ID);
    LuBootAssert.isTrue(config != null, "请检查流程配置");
    Long userId = null;
    List<Long> roleIds = null;
    if(request.getType() == ProcessApprovalQueryRequest.Type.WAIT.getType()){
      userId = SecurityUtil.getCurrentUser().getId();
      if(ObjectUtil.isNotEmpty(SecurityUtil.getCurrentUser().getRoleList())){
        roleIds = SecurityUtil.getCurrentUser().getRoleList().stream().map(SysRole::getId).collect(Collectors.toList());
      }
    }
    Long finalUserId = userId == null ? -1L : userId;
    List<Long> finalRoleIds = roleIds;
    return PageHelper.startPage(request.getPageIndex(), request.getPageSize())
        .doSelectPage(() -> logMapper.processApprovalLogs(request, config, finalUserId, finalRoleIds));
  }

  @Override
  public List<ProcessApprovalWaitList> waitList(ProcessApprovalQueryRequest request) {
    SysApprovalConfig approvalConfig = SysApprovalConfig.CONFIG_MAP.get(request.getTableName());
    LuBootAssert.isTrue(approvalConfig != null, "流程未配置");
    SysApprovalConfig config = SysApprovalConfig.get(approvalConfig, SysApprovalConfig.END_NODE_ID);
    LuBootAssert.isTrue(config != null, "请检查流程配置");
    Long userId = SecurityUtil.getCurrentUser().getId();
    List<Long> roleIds = null;
    if(ObjectUtil.isNotEmpty(SecurityUtil.getCurrentUser().getRoleList())){
      roleIds = SecurityUtil.getCurrentUser().getRoleList().stream().map(SysRole::getId).collect(Collectors.toList());
    }
    LuBootAssert.isFalse(ObjectUtil.isAllEmpty(userId, roleIds), "无审批权限");
    List<ProcessApprovalWaitList> waitList = logMapper.waitList(config, userId == null ? -1L : userId, roleIds);
    return waitList;
  }

  @Override
  public List<SysApprovalLog> detail(ProcessApprovalQueryRequest request) {
    LuBootAssert.isTrue(request.getObjId() != null, "请输入业务对象");
    QueryWrapper<SysApprovalLog> logQueryWrapper = new QueryWrapper<>();
    logQueryWrapper.eq("table_name", request.getTableName());
    logQueryWrapper.eq("obj_id", request.getObjId());
    List<SysApprovalLog> logs = logMapper.selectList(logQueryWrapper);
    for (SysApprovalLog log : logs) {
      SysUser user = userMapper.selectById(log.getUserId());
      log.setUser(user);
    }
    return logs;
  }

  @Override
  public <T> T get(ProcessApprovalQueryRequest request) {
    LuBootAssert.isTrue(request.getObjId() != null, "请输入业务对象");
    SysApprovalConfig approvalConfig = SysApprovalConfig.CONFIG_MAP.get(request.getTableName());
    LuBootAssert.isTrue(approvalConfig != null, "流程未配置");
    SysApprovalConfig config = SysApprovalConfig.get(approvalConfig, SysApprovalConfig.END_NODE_ID);
    LuBootAssert.isTrue(config != null, "请检查流程配置");
    BaseMapper baseMapper = null;
    try {
      baseMapper = (BaseMapper) SpringUtil.getBean(Class.forName(config.getMapperBean()));
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException();
    }
    return (T) baseMapper.selectById(request.getObjId());
  }

}
